#include "FFLogic.h"

bool FFLogic::save_frame_as_png(AVFrame* frame, AVCodecContext* png_ctx, int frame_index, const std::string& output_dir)
{
    AVPacket* pkt = av_packet_alloc();  // 分配压缩数据包
    if (!pkt) return false;

    // 将帧送入编码器
    int ret = avcodec_send_frame(png_ctx, frame);
    if (ret < 0) return false;

    // 从编码器中读取压缩后数据
    ret = avcodec_receive_packet(png_ctx, pkt);
    if (ret < 0) return false;

    // 构造输出文件名，如 output/0.png
    std::ostringstream oss;
    oss << output_dir << "/" << frame_index << ".png";

    // 将压缩数据写入文件
    FILE* f = fopen(oss.str().c_str(), "wb");
    if (!f) return false;
    fwrite(pkt->data, 1, pkt->size, f);
    fclose(f);

    av_packet_free(&pkt);  // 释放内存
    return true;
}

int FFLogic::videoToImagesPNG(const std::string& input_file, const std::string& output_dir, int interval_ms, int max_frames, const OutPutOptional *optional)
{
    AVFormatContext* fmt_ctx = nullptr;

    // 打开输入视频文件
    if (avformat_open_input(&fmt_ctx, input_file.c_str(), nullptr, nullptr) < 0) {
        std::cerr << "无法打开输入文件。\n";
        return -1;
    }

    // 读取视频流信息（例如帧率、时长等）
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
        std::cerr << "无法读取视频流信息。\n";
        return -1;
    }

    // 查找视频流的索引
    int video_stream_index = -1;
    for (unsigned i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    if (video_stream_index == -1) {
        std::cerr << "找不到视频流。\n";
        return -1;
    }

    // 获取视频解码器参数并创建解码器上下文
    AVCodecParameters* codecpar = fmt_ctx->streams[video_stream_index]->codecpar;
    const AVCodec* decoder = avcodec_find_decoder(codecpar->codec_id);
    AVCodecContext* codec_ctx = avcodec_alloc_context3(decoder);
    avcodec_parameters_to_context(codec_ctx, codecpar);
    avcodec_open2(codec_ctx, decoder, nullptr);

    // 准备 PNG 编码器上下文
    const AVCodec* png_codec = avcodec_find_encoder(AV_CODEC_ID_PNG);
    AVCodecContext* png_ctx = avcodec_alloc_context3(png_codec);
    png_ctx->bit_rate = codec_ctx->bit_rate;
    png_ctx->width = codec_ctx->width;
    png_ctx->height = codec_ctx->height;
    png_ctx->pix_fmt = AV_PIX_FMT_RGB24;  // PNG 只支持 RGB24
    png_ctx->time_base = {1, 25};  // 时间基
    avcodec_open2(png_ctx, png_codec, nullptr);

    // 初始化像素格式转换器（从原始像素格式转为 RGB24）
    SwsContext* sws_ctx = sws_getContext(
        codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,  // 输入参数
        codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,    // 输出参数
        SWS_BILINEAR, nullptr, nullptr, nullptr);  // 插值算法

    // 分配帧结构
    AVFrame* frame = av_frame_alloc();      // 解码后的原始帧
    AVFrame* rgb_frame = av_frame_alloc();  // RGB 图像帧（用于保存为 PNG）

    // 分配 RGB 图像帧的 buffer 空间
    int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codec_ctx->width, codec_ctx->height, 1);
    uint8_t* buffer = (uint8_t*)av_malloc(num_bytes);
    av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, buffer, AV_PIX_FMT_RGB24,
                        codec_ctx->width, codec_ctx->height, 1);

    AVPacket* pkt = av_packet_alloc();  // 读取数据用的压缩包
    int frame_count = 0;  // 已处理帧数
    int saved_count = 0;  // 已保存帧数
    // 每隔 interval_ms 毫秒保存一帧
    int64_t interval_us = interval_ms * 1000; // 抽帧间隔（微秒）
    int64_t last_saved_pts_us = -interval_us; // 上一帧保存时间（初始化为负值）

    // 主循环：读取每一个压缩包
    while (av_read_frame(fmt_ctx, pkt) >= 0) {
        if (pkt->stream_index == video_stream_index) {
            // 发送压缩数据包到解码器
            if (avcodec_send_packet(codec_ctx, pkt) == 0) {
                // 接收解码后图像帧
                while (avcodec_receive_frame(codec_ctx, frame) == 0) {
                    // 获取当前帧的时间戳（微秒）
                    int64_t pts_us = av_rescale_q(frame->best_effort_timestamp,
                                                fmt_ctx->streams[video_stream_index]->time_base,
                                                AVRational{1, 1000000});  // 转为微秒

                    // 判断是否满足抽帧条件
                    if (pts_us - last_saved_pts_us >= interval_us) {
                        last_saved_pts_us = pts_us;

                        // 转换像素格式（YUV -> RGB）
                        sws_scale(sws_ctx, frame->data, frame->linesize, 0,
                                codec_ctx->height, rgb_frame->data, rgb_frame->linesize);

                        // 设置 RGB 帧元数据
                        rgb_frame->format = AV_PIX_FMT_RGB24;
                        rgb_frame->width = codec_ctx->width;
                        rgb_frame->height = codec_ctx->height;

                        // 保存为 PNG 文件
                        save_frame_as_png(rgb_frame, png_ctx, saved_count, output_dir);
                        saved_count++;

                        // 若指定了最大帧数，提前退出
                        if (max_frames > 0 && saved_count >= max_frames) {
                            break;
                        }
                    }
                }
            }
        }
        av_packet_unref(pkt);  // 清除 pkt 结构体，准备下一轮
    }

    // 释放资源
    av_packet_free(&pkt);
    av_frame_free(&frame);
    av_frame_free(&rgb_frame);
    avcodec_free_context(&codec_ctx);
    avcodec_free_context(&png_ctx);
    sws_freeContext(sws_ctx);
    avformat_close_input(&fmt_ctx);
    av_free(buffer);

    return 0;
}

int FFLogic::videoToImagesJPG(const std::string& videoPath, const std::string& outputDir, int intervalMs, const OutPutOptional *optional) {
    AVFormatContext* fmtCtx = nullptr;
    if (avformat_open_input(&fmtCtx, videoPath.c_str(), nullptr, nullptr) < 0) {
        std::cerr << "Failed to open video: " << videoPath << std::endl;
        return -1;
    }

    if (avformat_find_stream_info(fmtCtx, nullptr) < 0) {
        std::cerr << "Failed to find stream info." << std::endl;
        avformat_close_input(&fmtCtx);
        return -2;
    }

    int videoStream = -1;
    for (unsigned i = 0; i < fmtCtx->nb_streams; i++) {
        if (fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    }

    if (videoStream == -1) {
        std::cerr << "No video stream found." << std::endl;
        avformat_close_input(&fmtCtx);
        return -3;
    }

    AVCodecParameters* codecpar = fmtCtx->streams[videoStream]->codecpar;
    const AVCodec* decoder = avcodec_find_decoder(codecpar->codec_id);
    AVCodecContext* decoderCtx = avcodec_alloc_context3(decoder);
    avcodec_parameters_to_context(decoderCtx, codecpar);
    avcodec_open2(decoderCtx, decoder, nullptr);

    // JPEG encoder
    const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
    if (!jpegCodec) {
        std::cerr << "JPEG encoder not found!" << std::endl;
        return -4;
    }

    AVCodecContext* jpegCtx = avcodec_alloc_context3(jpegCodec);
    jpegCtx->bit_rate = 0;  // 显式设置为0，表示不使用bit_rate控制质量
    jpegCtx->flags |= AV_CODEC_FLAG_QSCALE;  // 使用qscale模式
    jpegCtx->global_quality = FF_QP2LAMBDA * 2;  // QP=2，图像质量高，范围一般是1~31
    // jpegCtx->width = decoderCtx->width;
    // jpegCtx->height = decoderCtx->height;
    // 我靠 人脸图片图片还必须要480x640分辨率的图
    jpegCtx->width = ((optional != nullptr) && (optional->outputWidth > 0)) ? optional->outputWidth : decoderCtx->width;
    jpegCtx->height = ((optional != nullptr) && (optional->outputHeight > 0)) ? optional->outputHeight : decoderCtx->height;
    jpegCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;  // JPEG支持的格式
    jpegCtx->time_base = {1, 25};

    if (avcodec_open2(jpegCtx, jpegCodec, nullptr) < 0) {
        std::cerr << "Failed to open JPEG encoder." << std::endl;
        return -5;
    }

    AVPacket* packet = av_packet_alloc();
    AVFrame* frame = av_frame_alloc();
    AVFrame* yuvFrame = av_frame_alloc();

    // 输出图像帧所需的缓冲
    yuvFrame->format = jpegCtx->pix_fmt;
    yuvFrame->width = jpegCtx->width;
    yuvFrame->height = jpegCtx->height;
    av_frame_get_buffer(yuvFrame, 0);

    SwsContext* swsCtx = sws_getContext(
        decoderCtx->width, decoderCtx->height, decoderCtx->pix_fmt,
        jpegCtx->width, jpegCtx->height, jpegCtx->pix_fmt,
        SWS_BICUBIC, nullptr, nullptr, nullptr);

    int64_t lastPtsMs = 0;
    int frameCount = 0;

    AVRational timeBase = fmtCtx->streams[videoStream]->time_base;

    while (av_read_frame(fmtCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            if (avcodec_send_packet(decoderCtx, packet) >= 0) {
                while (avcodec_receive_frame(decoderCtx, frame) >= 0) {
                    int64_t ptsMs = av_rescale_q(frame->pts, timeBase, {1, 1000});
                    if (ptsMs - lastPtsMs >= intervalMs) {
                        sws_scale(swsCtx, frame->data, frame->linesize, 0,
                                  decoderCtx->height, yuvFrame->data, yuvFrame->linesize);

                        // 编码 JPEG
                        AVPacket jpgPkt;
                        av_init_packet(&jpgPkt);
                        jpgPkt.data = nullptr;
                        jpgPkt.size = 0;

                        if (avcodec_send_frame(jpegCtx, yuvFrame) >= 0) {
                            if (avcodec_receive_packet(jpegCtx, &jpgPkt) >= 0) {
                                char filename[512];
                                snprintf(filename, sizeof(filename), "%s/%010d.jpg", outputDir.c_str(), frameCount++);
                                FILE* outFile = fopen(filename, "wb");
                                if (outFile) {
                                    fwrite(jpgPkt.data, 1, jpgPkt.size, outFile);
                                    fclose(outFile);
                                }
                                av_packet_unref(&jpgPkt);
                            }
                        }

                        lastPtsMs = ptsMs;
                    }
                }
            }
        }
        av_packet_unref(packet);
    }

    sws_freeContext(swsCtx);
    av_frame_free(&frame);
    av_frame_free(&yuvFrame);
    av_packet_free(&packet);
    avcodec_free_context(&decoderCtx);
    avcodec_free_context(&jpegCtx);
    avformat_close_input(&fmtCtx);

    return 0;
}

int FFLogic::videoToImages(const string &logName, const CRM::VideoToImagesReq &req, CRM::VideoToImagesRsp &rsp)
{
    auto startTime = TNOWMS;
    int ret = 0;
    string input_file, output_dir;

    std::ostringstream oss;
    oss << "{";
    oss << "\"flags\":" << req.flags << ",";
    oss << "\"reqDataType\":" << req.reqDataType << ",";
    oss << "\"rspDataType\":" << req.rspDataType << ",";
    oss << "\"intervalMs\":" << req.intervalMs << ",";

    // 截断 base64 字符串
    std::string base64Short = (req.videoBase64.length() <= 32 ? req.videoBase64 : req.videoBase64.substr(0, 32));
    oss << "\"videoBase64\":\"" << base64Short << "...\"" << ",";

    // 仅输出二进制数据大小
    oss << "\"videoStream\":\"[binary data: size=" << req.videoStream.size() << "]\"" << ",";

    oss << "\"outputWidth\":" << req.outputWidth << ",";
    oss << "\"outputHeight\":" << req.outputHeight;

    oss << "}";

    FLOG_INFO(logName) << "{videoToImagesRequest}|" << oss.str() << endl;
    if (req.flags == 1)
    {
        // 1. 生成临时路径
        string tempRootDir = "/tmp/FFVidClipTempFile";
        mkdir(tempRootDir.c_str(), 0755);  // 如果已存在，会直接返回，不影响

        input_file = tempRootDir + "/" + gen_random(16) + ".mp4";
        output_dir = tempRootDir + "/" + gen_random(16);
        mkdir(output_dir.c_str(), 0755);  // 创建子目录
        FLOG_INFO(logName) << "{request input_file^output_dir}|" << input_file << "|" << output_dir << endl;

        // 2. 写视频文件
        ofstream ofs(input_file.c_str(), ios::binary);
        if (!ofs) {
            ret = -1;
            goto exit;
        }

        if (req.reqDataType == 0)
        {
            ofs.write(reinterpret_cast<const char *>(req.videoStream.data()), req.videoStream.size());
        }
        else if (req.reqDataType == 1)
        {
            string vidStream = TC_Base64::decode(req.videoBase64);
            ofs.write(vidStream.c_str(), vidStream.size());
        }
        ofs.close();

        // 3. 抽帧
        struct OutPutOptional outOpt;
        outOpt.outputWidth = req.outputWidth;
        outOpt.outputHeight = req.outputHeight;
        // int ret = videoToImagesPNG(input_file.c_str(), output_dir.c_str(), req.intervalMs);
        int ret = videoToImagesJPG(input_file.c_str(), output_dir.c_str(), req.intervalMs, &outOpt);
        FLOG_INFO(logName) << "{videoToImages input_file^output_dir^intervalMs^ret}|"
            << input_file << "|"
            << output_dir << "|"
            << req.intervalMs << "|"
            << ret << endl;
        if (ret != 0) {
            ret = -2;
            goto exit;
        }

        // 4. 读取输出目录中所有 PNG 文件
        DIR *dir = opendir(output_dir.c_str());
        FLOG_INFO(logName) << "{opendir dir}|" << dir << endl;
        if (!dir) {
            ret = -3;
            goto exit;
        }

        struct dirent *entry;
        std::ostringstream ossLog;
        vector<pair<string, vector<uint8_t>>> sortedFrames;
        while ((entry = readdir(dir)) != nullptr)
        {
            string full_path = output_dir + "/" + entry->d_name;
            ossLog << "|" << full_path;
            // 只处理目录中的普通文件（DT_REG）
            if (entry->d_type == DT_REG)
            {
                ifstream ifs(full_path.c_str(), ios::binary);
                vector<uint8_t> frameData((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());
                sortedFrames.emplace_back(entry->d_name, std::move(frameData));
            }
        }
        FLOG_INFO(logName) << "{entry full_path}" << ossLog.str() << endl;
        closedir(dir);
        FLOG_INFO(logName) << "{closedir dir}|" << dir << endl;

        // 按文件名中的数字部分排序，例如 "10.后缀名称" > "2.后缀名称"
        std::sort(sortedFrames.begin(), sortedFrames.end(), [](const pair<string, vector<uint8_t>> &a, const pair<string, vector<uint8_t>> &b) {
            int numA = std::stoi(a.first.substr(0, a.first.find(".")));
            int numB = std::stoi(b.first.substr(0, b.first.find(".")));
            return numA < numB;
        });

        // 填充响应
        if (req.rspDataType == 0)
        {
            ostringstream frameSortedLog;
            for (size_t i = 0; i < sortedFrames.size(); ++i)
            {
                const std::string &filename = sortedFrames[i].first;
                const std::vector<uint8_t> &data = sortedFrames[i].second;
                frameSortedLog << "|" << filename;
                rsp.framesStreamList.push_back(std::vector<char>(data.begin(), data.end()));
            }
            FLOG_INFO(logName) << "{sortedFrames frameSortedLog}" << frameSortedLog.str() << endl;
        }
        else if (req.rspDataType == 1)
        {
            ostringstream frameSortedLog;
            for (size_t i = 0; i < sortedFrames.size(); ++i)
            {
                const std::string &filename = sortedFrames[i].first;
                const std::vector<uint8_t> &data = sortedFrames[i].second;
                std::string base64Str = TC_Base64::encode(reinterpret_cast<const char *>(data.data()), data.size());
                frameSortedLog << "|" << filename;
                rsp.framesBase64List.push_back(base64Str);
            }
            FLOG_INFO(logName) << "{sortedFrames frameSortedLog}" << frameSortedLog.str() << endl;
        }
    }
    else
    {
        // int interval_ms = req.intervalMs;
        // if (interval_ms <= 0) interval_ms = 1;
        // int result = extract_frames_from_memory(req.videoStream, interval_ms, 0, rsp.framesStreamList);
        // if (result != 0) {
        //     std::cerr << "内存抽帧失败！\n";
        //     return -1;
        // }
    }

exit:
    // 5. 删除 input_file 和 output_dir
    if (!input_file.empty()) {
        remove(input_file.c_str());
        FLOG_INFO(logName) << "{remove input_file}|" << input_file << endl;
    }
    if (!output_dir.empty()) {
        delete_dir_and_files(output_dir);
        FLOG_INFO(logName) << "{delete_dir_and_files output_dir}|" << output_dir << endl;
    }

    oss.str("");
    oss << "{";
    oss << "\"ret\":" << rsp.ret << ",";
    oss << "\"msg\":\"" << rsp.msg << "\",";
    oss << "\"framesBase64List\":\"" << rsp.framesBase64List.size() << "\",";
    oss << "\"framesStreamList\":\"" << rsp.framesStreamList.size() << "\"";
    oss << "}";
    FLOG_INFO(logName) << "{videoToImagesResponse}|" << oss.str() << "|" << TNOWMS - startTime << "ms" << endl;
    return ret;
}
